////////////////////////////////////////////////////////////////////////
// This file is part of the simpleSID framework 
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
//
// Copyright (c)Richard White, 2009
// See License.txt for a disclaimer of all warranties
// and licensing information

#include <stdafx.h>
#include "Voice.h"

const unsigned int Voice::m_dwMaskActiveTable[2] = {	0x00000000, 0xffffffff	};

Voice::Voice()
:	m_byFreqLO(0), 
	m_byFreqHI(0), 
	m_byPWLO(0), 
	m_byPWHI(0), 
	m_byControl(0), 
	m_wFreq(0), 
	m_wPulseWidth(0), 
	m_bNoise(false), 
	m_bPulse(false), 
	m_bRingMod(false),
	m_bMSBRising(false),
	m_bSaw(false), 
	m_bSync(false), 
	m_bTest(false), 
	m_bTri(false), 
	m_dwAccumulator(0), 
	m_dwShift(0x7ffffc),
	m_nMute(1),
	m_nMSBRising(0)
{
	
	Reset();
	m_VoiceSncSource = this;
}

Voice::~Voice()
{

}

void Voice::SetFreqLO(unsigned char byFreqLO)
{
	m_byFreqLO = byFreqLO;
	
	m_wFreq = m_wFreq & 0xff00 | byFreqLO & 0x00ff;
}

void Voice::SetFreqHI(unsigned char byFreqHI)
{
	m_byFreqHI = byFreqHI;

	m_wFreq = (byFreqHI << 8) & 0xff00 | m_wFreq & 0x00ff;
}

void Voice::SetPWLO(unsigned char byPWLO)
{
	m_byPWLO = byPWLO;

	m_wPulseWidth = m_wPulseWidth & 0xf00 | m_byPWLO & 0x0ff;
}

void Voice::SetPWHI(unsigned char byPWHI)
{
	m_byPWHI = byPWHI;

	m_wPulseWidth = (m_byPWHI << 8) & 0xf00 | m_wPulseWidth & 0x0ff;
}

void Voice::SetControlReg(unsigned char byControlReg)
{
	int bNextTest = byControlReg & 0x08;

	if (bNextTest)
	{
		m_dwAccumulator = 0;
		m_dwShift = 0;
	}
	else if (m_bTest)
		m_dwShift = 0x7ffffc;

	m_byControl = byControlReg;
	//Need to add checking for waves and bits set

	if (m_byControl & 0x01)
		m_Envelope.SetGate(true);
	else
		m_Envelope.SetGate(false);

	if (m_byControl & 0x02)
		m_bSync = true;
	else
		m_bSync = false;

	if (m_byControl & 0x04)
		m_bRingMod = true;
	else
		m_bRingMod = false;

	if (m_byControl & 0x08)
		m_bTest = true;
	else
		m_bTest = false;

	if (m_byControl & 0x10)
		m_bTri = true;
	else
		m_bTri = false;

	if (m_byControl & 0x20)
		m_bSaw = true;
	else
		m_bSaw = false;

	if (m_byControl & 0x40)
		m_bPulse = true;
	else
		m_bPulse = false;

	if (m_byControl & 0x80)
		m_bNoise = true;
	else
		m_bNoise = false;

	
}

void Voice::SetAttackDecay(unsigned char byAD)
{
	m_byAttackDecay = byAD;
	m_Envelope.SetAttackDecay(byAD);
}

void Voice::SetSustainRelease(unsigned char bySR)
{
	m_bySustainRelease = bySR;
	m_Envelope.SetSustainRelease(bySR);
}

void Voice::SetSyncSource(Voice *voice)
{
	m_VoiceSncSource = voice;
	voice->m_VoiceSncDest = this;
}

void Voice::SetSawWave(bool bSaw)
{
	m_bSaw = bSaw;
}

void Voice::SetPulseWave(bool bPulse)
{
	m_bPulse = bPulse;
}

void Voice::SetTriWave(bool bTri)
{
	m_bTri = bTri;
}

void Voice::SetNoiseWave(bool bNoise)
{
	m_bNoise = bNoise;
}

void Voice::SetTestBit(bool bTest)
{
	if (bTest)
	{
		m_dwAccumulator = 0;
		m_dwShift = 0;
	}
	else if (m_bTest)
		m_dwShift = 0x7ffffc;

	m_bTest = bTest;
}

void Voice::SetRingModBit(bool bRing)
{
	m_bRingMod = bRing;
}

void Voice::SetSyncBit(bool bSync)
{
	m_bSync = bSync;
}

void Voice::SetAttack(unsigned char byAttack)
{
	m_Envelope.SetAttack(byAttack);
}

void Voice::SetDecay(unsigned char byDecay)
{
	m_Envelope.SetDecay(byDecay);
}

void Voice::SetSustain(unsigned char bySustain)
{
	m_Envelope.SetSustain(bySustain);
}

void Voice::SetRelease(unsigned char byRelease)
{
	m_Envelope.SetRelease(byRelease);
}

void Voice::SetGate(bool bGate)
{
	m_Envelope.SetGate(bGate);
}

void Voice::SetMuteState(bool bState)
{
	m_nMute = (bState) ? 0 : 1;
}

bool Voice::GetMuteState()
{
	return (m_nMute) ? true : false;
}

unsigned char Voice::GetFreqLO()
{
	return m_byFreqLO;
}

unsigned char Voice::GetFreqHI()
{
	return m_byFreqHI;
}

unsigned char Voice::GetPWLO()
{
	return m_byPWLO;
}

unsigned char Voice::GetPWHI()
{
	return m_byPWHI;
}

unsigned char Voice::GetControl()
{
	return m_byControl;
}

unsigned char Voice::GetAttackDecay()
{
	return m_byAttackDecay;
}

unsigned char Voice::GetSustainRelease()
{
	return m_bySustainRelease;
}


unsigned char Voice::GetAttack()
{
	return m_Envelope.GetAttack();
}

unsigned char Voice::GetDecay()
{
	return m_Envelope.GetDecay();
}

unsigned char Voice::GetSustain()
{
	return m_Envelope.GetSustain();
}

unsigned char Voice::GetRelease()
{
	return m_Envelope.GetRelease();
}

unsigned short Voice::GetFreq()
{
	return m_wFreq;
}

unsigned short Voice::GetPW()
{
	return m_wPulseWidth;
}

unsigned int Voice::GetAccumulator()
{
	return m_dwAccumulator;
}

bool Voice::GetNoiseOn()
{
	return m_bNoise;
}

bool Voice::GetPulseOn()
{
	return m_bPulse;
}

bool Voice::GetSawOn()
{
	return m_bSaw;
}

bool Voice::GetTriOn()
{
	return m_bTri;
}

bool Voice::GetTestOn()
{
	return m_bTest;
}

bool Voice::GetRingModOn()
{
	return m_bRingMod;
}

bool Voice::GetSyncOn()
{
	return m_bSync;
}

void Voice::Reset()
{
	m_byFreqLO = 0;
	m_byFreqHI = 0;
	m_byPWLO = 0;
	m_byPWHI = 0;
	m_byControl = 0;
	m_wFreq = 0;
	m_wPulseWidth = 0;
	m_byAttackDecay = 0;
	m_bySustainRelease = 0;
	m_bNoise = false;
	m_bPulse = false;
	m_bRingMod = false; 
	m_bSaw = false;
	m_bSync = false;
	m_bTest = false;
	m_bTri = false;
	m_bMSBRising = false;
	m_nMSBRising = 0;

	m_dwAccumulator = 0;
	m_dwShift = 0x7ffffc; //this could be wrong will need to check
	
	m_nMute = 1;

	m_Envelope.Reset();
}
////////////////////////////////////////////////////////////////////////////////////////////
//		This function processes one cycle of the cpu									  //
////////////////////////////////////////////////////////////////////////////////////////////
void Voice::Process()
{
	//Clock the envelopes first
	m_Envelope.Process();

	if (m_bTest)
		return;
	
	//Store our previous Accumulator
	unsigned int dwPrevAccumulator = m_dwAccumulator;
	
	//Increment our Accumulator
	m_dwAccumulator += m_wFreq;
	m_dwAccumulator = m_dwAccumulator & 0xffffff;
	
	//if ((~dwPrevAccumulator & m_dwAccumulator) & 0x800000) //Can reactivate this another time
		//m_bMSBRising = true;
		
	//if (!(dwPrevAccumulator & 0x800000) && (m_dwAccumulator & 0x800000)) //try again
		//m_bMSBRising = true;
	
	m_nMSBRising = !(dwPrevAccumulator & 0x800000) && (m_dwAccumulator & 0x800000);

	//We are checking if the bit 19 is high and if so shift the noise register once each time the bit is set high
	if (!(dwPrevAccumulator & 0x080000) && (m_dwAccumulator & 0x080000))
	{
		//Voice.Noise:=((Voice.Noise shl 1) and $7fffff) or (((Voice.Noise shr 22) xor (Voice.Noise shr 17)) and 1);
		m_dwShift = ((m_dwShift << 1) & 0x7fffff) | (((m_dwShift >> 22) ^ (m_dwShift >> 17)) & 0x1);
	}

}

void Voice::Process(int iCycles) //TODO: Change this from float to a int
{
	//Lets loop for each cycle that needs to be done
	for (int i = 0; i < iCycles; i++)
	{
		Process();
	}
}

void Voice::Synchronize()
{
	//m_VoiceSncDest->m_dwAccumulator = m_VoiceSncDest->m_dwAccumulator & m_dwMaskActiveTable[!(m_bMSBRising && m_VoiceSncDest->m_bSync && !(m_bSync && m_VoiceSncSource->m_bMSBRising))]; //This is from bero have to test
	//if (m_bMSBRising && m_VoiceSncDest->m_bSync && !(m_bSync && m_VoiceSncSource->m_bMSBRising))
		//m_VoiceSncDest->m_dwAccumulator = 0;

	if (m_nMSBRising && m_VoiceSncDest->m_bSync && !(m_bSync && m_VoiceSncSource->m_nMSBRising)) 
	{
		m_VoiceSncDest->m_dwAccumulator = 0;
	}
} 

unsigned char Voice::ReadOsc()
{
	return Output() >> 4; //Shift it down to 8 bit as it is a 8 bit register
}

unsigned char Voice::ReadEnvelope()
{
	return m_Envelope.Output(); //8bit out of envelope used by third voice
}

int Voice::Output()
{
	int iSaw = 0; // Oh what an eye saw
	int iTri = 0;
	int iPulse = 0;
	int iNoise = 0;
	int iOut = 0xfff;
	//We need a check to check the phase of the sync voice and we also need to do ring mod
	
	//Create intermediate values to be used for osc generation
	int dwAccumulator = m_dwAccumulator;
	int dwIntermediate = dwAccumulator >> 12;
	
	//Saw is just Accumulator >> 12
	iSaw = dwIntermediate;
	
	//Pulse is Accumulator >> 12 and compared to its pulse width value. if its more set to DC level. Also do this if test bit is set
	if ((dwIntermediate >= m_wPulseWidth) || m_bTest)
		iPulse = 0x000fff;
	else
		iPulse = 0x000000;
	
	//The Triangle Wave uses 12 bits just like the others. Except It checks whether
	//The msb has been set. If it is it knows to create the falling part of the triangle
	//wave. It then discards the MSB is discareded and the Accumulator is shifted left 11 bits
	//And if the MSB was set it then inverts the remaining bits. Else it just outputs
	//If the ring mod bit is set it checks whether the MSB is set by exclusive oring 
	//of the accumulator with the sync source.
	bool bMSB;
	if (m_bRingMod)
	{
		if ((m_dwAccumulator ^ m_VoiceSncSource->m_dwAccumulator) & 0x800000)
			bMSB = true;
		else
			bMSB = false;
	}
	else
	{
		if (m_dwAccumulator & 0x800000)
			bMSB = true;
		else
			bMSB = false;
	}

	if (bMSB)
		iTri = (((~m_dwAccumulator) >> 11) & 0xfff);
	else
		iTri = ((m_dwAccumulator >> 11) & 0xfff);
	
	//int msb = (m_bRingMod ? m_dwAccumulator ^ m_VoiceSncSource->m_dwAccumulator : m_dwAccumulator) & 0x800000;
	//iTri = ((msb ? ~m_dwAccumulator : m_dwAccumulator) >> 11) & 0xfff;

	//Noise generation via shift register it is clocked by bit 19 of the accumulator
	//
	iNoise = (  ((m_dwShift & 0x400000) >> 11) | 
				((m_dwShift & 0x100000) >> 10) |
				((m_dwShift & 0x010000) >> 7)  |
				((m_dwShift & 0x002000) >> 5)  |
				((m_dwShift & 0x000800) >> 4)  |
				((m_dwShift & 0x000080) >> 1)  |
				((m_dwShift & 0x000010) << 1)  |
				((m_dwShift & 0x000004) << 2) );
	
	//We might neede to change the anding here?
	if (m_bTri)
		iOut = iTri;
	if (m_bSaw)
		iOut = iSaw;
	if (m_bPulse)
		iOut = iPulse;
	if (m_bNoise)
		iOut = iNoise;
	
	//The 0x800 offset is so as to make the osc out go from +2048 to -2048 or +1 to -1 normalised instead of 0 - 4096 
	int iMain = ((iOut - 0x800) * m_Envelope.Output()) * m_nMute;

	return iMain;
}
